今天討論的是 Ascii Filter 與實作
前陣子看朋友在討論這個案例,
覺得 Ascii Filter 效果很適合在這個系列挑篇文章討論
因此將 Ascii Filter 分做 Canvas 原理 、 WebGL 原理 兩部分討論
並調整 Shadertoy 裡 Ascii Filter 的程式碼
PixiJS 有相同濾鏡,調整方式也幾乎相同
顏色分佈密度
給予不同圖樣實作方式因人而異,但大多會有類似的程式碼:
var oImgData = oCtx.getImageData( 0, 0, iWidth, iHeight ).data;
var strChars = "";
for ( var y = 0; y < iHeight; y += 2 ) {
for ( var x = 0; x < iWidth; x ++ ) {
var iOffset = ( y * iWidth + x ) * 4;
var iRed = oImgData[ iOffset ];
var iGreen = oImgData[ iOffset + 1 ];
var iBlue = oImgData[ iOffset + 2 ];
var iAlpha = oImgData[ iOffset + 3 ];
var fBrightness;
fBrightness = ( 0.3 * iRed + 0.59 * iGreen + 0.11 * iBlue ) / 255;
if ( iAlpha == 0 ) {
fBrightness = 1;
}
}
}
canvas
版本的 Ascii 濾鏡會使用 HTML canvas getImageData() Method:
抓取特定範圍內 4
個色版的顏色
由於 getImageData() 回傳的 ImageData 包含4
個色版的顏色,
因此可以計算區域內的圖像填滿的程度
red=imgData.data[0];
green=imgData.data[1];
blue=imgData.data[2];
alpha=imgData.data[3];
three.js 的 three.js - ascii filter example 範例,也是使用 Canvas 判斷
濾鏡原始檔: three.js ascii effect
也是抓區域裡 RGB
的值做運算:
vec2 pix = fragCoord.xy;
vec3 col = texture(iChannel0, floor(pix/8.0)*8.0/iResolution.xy).rgb;
float gray = 0.3 * col.r + 0.59 * col.g + 0.11 * col.b;
float n = 4096; // .
if (gray > 0.2) n = 65600.; // :
if (gray > 0.3) n = 332772.; // *
//…
寫法看起來不太相同,原理幾乎相同
Shadertoy - Ascii Art:
vec2 uv = fragCoord/iResolution.xy;
vec3 col = texture(iChannel0, uv).rgb;
fragColor = vec4(col,1.0);
輸出:
float n = 4096; // .
if (gray > 0.2) n = 65600.; // :
if (gray > 0.3) n = 332772.; // *
if (gray > 0.4) n = 15255086.; // o
if (gray > 0.5) n = 23385164.; // &
if (gray > 0.6) n = 15252014.; // 8
if (gray > 0.7) n = 13199452.; // @
if (gray > 0.8) n = 11512810.; // #
顏色分佈越鬆散的,放入的符號越單純 (如.
)
顏色分佈越密集的,放入的符號越複雜 (如#
)
ViiSUALiiZER 網站裡,使用的是 i,
符號來排列:
i,
小工具 ASCII tool:
可以用勾選的方式把想要的文字 點
出來成 int
使用:
點出想要的 i,
形狀,輸出的 int
25436541 接下來使用
輸入25436541 時,輸出的
i
會上下相反
於是點了個上下相反的i
,取得int
24971331
在 Shadertoy 裡將原本 float n
的部分註解掉,
改成 gray > 0.5 時輸出 i,
(n=24971331),其他不輸出:
// float n = 4096; // .
// if (gray > 0.2) n = 65600.; // :
// if (gray > 0.3) n = 332772.; // *
// if (gray > 0.4) n = 15255086.; // o
// if (gray > 0.5) n = 23385164.; // &
// if (gray > 0.6) n = 15252014.; // 8
// if (gray > 0.7) n = 13199452.; // @
// if (gray > 0.8) n = 11512810.; // #
n = 0;
if (gray > 0.5) n = 24971331;
現在是只輸出 i,
了!
原本輸出的值是包含 R
、G
、B
三種顏色,
輸出單色時,最簡單的做法就是...
三個色版輸出相同顏色 ( col.r
、 col.g
或 col.b
)
// fragColor = vec4(col, 1.0);
fragColor = vec4(vec3(col.r), 1.0);
將原本輸出的 fragColor 註解掉,改成輸出成 vec4(vec3(col.r), 1.0)
vec3(col.r
) 等同於輸出 vec3(col.r
, col.r
, col.r
)
RGB的值皆相同時輸出單色,例如 #000
~ #FFF
調整結果:
此時輸出的畫面是黑底反白
,如果希望是白底反黑
時:
將剛剛的顏色翻過來就好!
fragColor = vec4(vec3(1.-col.r), 1.0);
由於 Shader 裡顏色的範圍是 0.
~ 1.
,
因此要直接反白的時候,用 1. 減掉顏色 即可
也就是剛剛 vec3(1.
-col.r
)
調整結果:
這段 Shadertoy 程式碼裡有一段註解
// Bitmap to ASCII (not really) fragment shader by movAX13h, September 2013
// This is the original shader that is now used in PixiJs, FL Studio and various other products.
PixiJS 的 Ascii Filter 是這段 Fragment Shader 的實作,也可以依照本文調整喔
PixiJS Filters Demo